package cn.wolfcode.web.controller;

import cn.wolfcode.common.exception.BusinessException;
import cn.wolfcode.common.util.AssertUtils;
import cn.wolfcode.common.web.CodeMsg;
import cn.wolfcode.common.web.Result;
import cn.wolfcode.config.AlipayProperties;
import cn.wolfcode.domain.PayResult;
import cn.wolfcode.domain.PayVo;
import cn.wolfcode.domain.RefundVo;
import cn.wolfcode.feign.SeckillFeignClient;
import cn.wolfcode.web.msg.PayCodeMsg;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeFastpayRefundQueryRequest;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;


@RestController
@RequestMapping("/alipay")
@Slf4j
public class AlipayController {

    @Autowired
    private AlipayClient alipayClient;

    @Autowired
    private AlipayProperties alipayProperties;

    @Resource
    private SeckillFeignClient seckillFeignClient;

    /**
     * 支付宝支付
     */
    @PostMapping("/prePay")
    public Result<String> prePay(@RequestBody PayVo payVo) {
        // 利用支付宝sdk api 向支付宝发送支付请求
        try {
            AlipayTradePagePayRequest request = this.buildRequest(payVo);
            //请求
            String body = alipayClient.pageExecute(request).getBody();
            // 请求到支付宝以后，预订单创建成功，会返回一个 HTML 片段，实现跳转到支付宝页面
            log.info("[支付宝支付] 支付宝成功发起支付，收到支付响应result:\n{}", body);
            return Result.success(body);
        } catch (Exception e) {
            e.printStackTrace();
            return Result.error(PayCodeMsg.PAY_FAILED);
        }
    }

    /**
     * 设置支付宝支付请求参数
     */
    private AlipayTradePagePayRequest buildRequest(PayVo vo) {
        // 设置请求参数
        AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
        alipayRequest.setReturnUrl(vo.getReturnUrl());
        alipayRequest.setNotifyUrl(vo.getNotifyUrl());

        JSONObject json = new JSONObject();
        json.put("out_trade_no", vo.getOutTradeNo());
        json.put("total_amount", vo.getTotalAmount());
        json.put("subject", vo.getSubject());
        json.put("body", vo.getBody());
        json.put("product_code", "FAST_INSTANT_TRADE_PAY");

        alipayRequest.setBizContent(json.toJSONString());
        return alipayRequest;
    }

    /**
     * 异步回调
     */
    @PostMapping("/notify_url")
    public String notifyUrl(HttpServletRequest request) {
        // 接受支付宝的参数
        Map<String, String> params = new HashMap<>();
        Map<String, String[]> requestParams = request.getParameterMap();
        for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                        : valueStr + values[i] + ",";
            }
            // 乱码解决，这段代码在出现乱码时使用
            // valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put(name, valueStr);
        }

        try {
            log.info("[异步回调] 收到交易消息: {}", params);
            boolean verify_result = AlipaySignature.rsaCheckV1(params, alipayProperties.getAlipayPublicKey(),
                    alipayProperties.getCharset(), "RSA2");
            AssertUtils.isTrue(verify_result, "支付宝回调签名验证失败");

            //商户订单号
            String outTradeNo = request.getParameter("out_trade_no");
            //支付宝交易号
            String tradeNo = request.getParameter("trade_no");
            //交易状态
            String tradeStatus = request.getParameter("trade_status");
            //支付金额
            String totalAmount = request.getParameter("total_amount");

            if (tradeStatus.equals("TRADE_FINISHED")) {
                log.info("[异步回调] {}订单已完成...", outTradeNo);
            } else if (tradeStatus.equals("TRADE_SUCCESS")) {
                log.info("[异步回调] {}订单已支付成功...", outTradeNo);
                // 远程调用秒杀服务更新订单状态
                PayResult payResult = new PayResult(outTradeNo, tradeNo, totalAmount);
                Result<?> ret = seckillFeignClient.updateOrderPaySuccess(payResult);
                AssertUtils.isFalse(ret.hasError(), "更新订单支付成功失败");
            }
            return "success";
        } catch (AlipayApiException e) {
            log.error(e.getMessage());
        }
        return "fail";
    }

    /**
     * 同步回调
     */
    @GetMapping("/return_url")
    public void returnUrl(HttpServletRequest request, HttpServletResponse response) {
        // 获取支付宝GET过来反馈信息
        Map<String, String> params = new HashMap<>();
        Map<String, String[]> requestParams = request.getParameterMap();
        for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                        : valueStr + values[i] + ",";
            }
            // 乱码解决，这段代码在出现乱码时使用
            // valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put(name, valueStr);
        }

        try {
            log.info("[同步回调] 收到交易消息: {}", params);
            boolean verify_result = AlipaySignature.rsaCheckV1(params, alipayProperties.getAlipayPublicKey(),
                    alipayProperties.getCharset(), "RSA2");
            AssertUtils.isTrue(verify_result, "支付宝回调签名验证失败");
            //商户订单号
            String outTradeNo = request.getParameter("out_trade_no");
            response.sendRedirect("http://localhost/order_detail.html?orderNo=" + outTradeNo);
        } catch (Exception e) {
            log.error(e.getMessage());
        }

        try {
            response.getWriter().write("支付宝同步回调签名验证失败");
        } catch (IOException e) {
            log.error(e.getMessage());
        }
    }

    /**
     * 支付宝退款
     */
    @PostMapping("/alipay/refund")
    Result<Boolean> refund(@RequestBody RefundVo refundVo) {
        // 创建退款请求对象
        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();

        // 构建请求参数
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", refundVo.getOutTradeNo());
        bizContent.put("refund_amount", refundVo.getRefundAmount());
        bizContent.put("refund_reason", refundVo.getRefundReason());
        request.setBizContent(bizContent.toString());

        try {
            // 执行退款操作
            AlipayTradeRefundResponse response = alipayClient.execute(request);
            log.info("[支付宝退款] 获取退款响应结果：{}", JSON.toJSONString(response));

            AssertUtils.isTrue(response.isSuccess(), response.getSubMsg());

            // 判断是否支付退款成功
            if (!"Y".equalsIgnoreCase(response.getFundChange())) {
                // 如果未收到 fundChange=Y，也不代表退款一定失败，可以再次调用退款查询接口查询是否真正退款成功
                AlipayTradeFastpayRefundQueryRequest refundQueryRequest = new AlipayTradeFastpayRefundQueryRequest();
                bizContent = new JSONObject();
                bizContent.put("out_trade_no", refundVo.getOutTradeNo());
                bizContent.put("out_request_no", refundVo.getOutTradeNo());
                refundQueryRequest.setBizContent(bizContent.toString());

                AlipayTradeFastpayRefundQueryResponse refundQueryResponse = alipayClient.execute(refundQueryRequest);
                log.info("[支付宝接口] 查询退款结果：{}", JSON.toJSONString(refundQueryResponse));

                if (!"REFUND_SUCCESS".equals(refundQueryResponse.getRefundStatus())) {
                    // 查询到的也是退款失败
                    return Result.success(false);
                }
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
            return Result.error(new CodeMsg(506002, e.getMessage()));
        }
        return Result.success(true);
    }

}
